﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Text;

namespace Xceed.Silverlight.Data
{
  public abstract class SqlFilterExpressionFormatter : FilterExpressionFormatter
  {
    #region Constructors

    protected SqlFilterExpressionFormatter( 
      FilterExpressionBase expression, 
      DataSourceProvider dataSourceProvider )
      : base( expression )
    {
      if( dataSourceProvider == null )
        throw new ArgumentNullException( "dataSourceProvider" );

      m_dataSourceProvider = dataSourceProvider;
    }

    #endregion

    #region Parameters Protected Property

    protected List<KeyValuePair<string, object>> Parameters
    {
      get
      {
        return m_parameters;
      }
    }

    private List<KeyValuePair<string, object>> m_parameters = new List<KeyValuePair<string, object>>();

    #endregion

    #region Fields Property Property

    protected List<KeyValuePair<string, string>> Fields
    {
      get
      {
        return m_fields;
      }
    }

    private List<KeyValuePair<string, string>> m_fields = new List<KeyValuePair<string, string>>();

    #endregion

    public SqlFilterExpressionResult GetResult()
    {
      this.Visit();

      if( m_result.Target == PreProcessResult.TargetNone )
      {
        this.PartialResult.Append( this.GetEmptyResultExpression() );
      }

      return new SqlFilterExpressionResult( this.PartialResult.ToString(), m_parameters, m_fields );
    }

    protected override void Initialize()
    {
      m_parameters.Clear();
      m_parameterNumber = 0;

      base.Initialize();
    }

    protected sealed override FilterExpressionBase GetFirstExpression()
    {
      FilterExpressionBase expression = base.GetFirstExpression();
      FilterExpressionPreProcessor transformer = new FilterExpressionPreProcessor( expression, m_dataSourceProvider );

      m_result = transformer.GetResult();

      return m_result.Expression;
    }

    protected override string GetFormat()
    {
      return "({0})";
    }

    protected override string GetFormat( FilterExpression expression )
    {
      return "{0} {1} {2}";
    }

    protected override string GetFormat( AndFilterExpression expression )
    {
      return SqlFilterExpressionFormatter.GetFormat( expression, "And" );
    }

    protected override string GetFormat( OrFilterExpression expression )
    {
      return SqlFilterExpressionFormatter.GetFormat( expression, "Or" );
    }

    protected override string GetFormat( NotFilterExpression expression )
    {
      return "Not {0}";
    }

    protected override string GetMemberName( FilterExpression expression )
    {
      string memberName = expression.MemberName;
      string fieldId = this.CreateNewFieldId();

      m_fields.Add( new KeyValuePair<string, string>( fieldId, memberName ) );

      return fieldId;
    }

    protected override string GetFilterOperator( FilterExpression expression )
    {
      switch( expression.FilterOperator )
      {
        case FilterOperator.Equal:
          {
            if( expression.Value == null )
              return "is";

            return "=";
          }

        case FilterOperator.NotEqual:
          {
            if( expression.Value == null )
              return "is not";

            return "<>";
          }

        case FilterOperator.GreaterThan:
          return ">";

        case FilterOperator.GreaterThanOrEqual:
          return ">=";

        case FilterOperator.LessThan:
          return "<";

        case FilterOperator.LessThanOrEqual:
          return "<=";

        default:
          throw new ArgumentException( "An unknown filter operator has been specified.", "expression" );
      }
    }

    protected override string GetValue( FilterExpression expression )
    {
      object value = expression.Value;

      if( value == null )
        return this.GetNullValue();

      string parameterId = this.CreateNewParameterId();

      m_parameters.Add( new KeyValuePair<string, object>( parameterId, value ) );

      return parameterId;
    }

    protected override string GetNullValue()
    {
      return "null";
    }

    protected virtual string GetEmptyResultExpression()
    {
      return "1 <> 1";
    }

    protected virtual string CreateNewParameterId()
    {
      string result = "{" + m_parameterNumber + "}";
      m_parameterNumber++;

      return result;
    }

    protected virtual string CreateNewFieldId()
    {
      string result = "@field" + m_fieldNumber;
      m_fieldNumber++;

      return result;
    }

    private static string GetFormat( FilterExpressionOperator expression, string operatorGlyph )
    {
      StringBuilder sb = new StringBuilder();
      int count = expression.ChildExpressions.Count;

      for( int i = 0; i < count; i++ )
      {
        if( i > 0 )
        {
          sb.Append( " " ).Append( operatorGlyph ).Append( " " );
        }

        sb.Append( "{" ).Append( i ).Append( "}" );
      }

      return sb.ToString();
    }

    #region Private Fields

    private DataSourceProvider m_dataSourceProvider;
    private FilterExpressionPreProcessorResult m_result;
    private int m_parameterNumber;
    private int m_fieldNumber;

    #endregion
  }
}
